/*
 * hash.c
 *
 * Version 1.0
 * Author: rtp
 *
 * Description: hash map
 *
 */

#include <strings.h>
#include <stdio.h>
#include <stdlib.h>

#include "hash.h"
#include "linked_list.h"

unsigned int hash_function(char *key)
{
	int i,
	n = 4;	// bytes per char
	unsigned int hashval;
	unsigned int ival;

	char *p; // p lets us reference the integer value as a
	// character array instead of an integer.  This
	// is actually pretty bad style even though it
	// works.  You should try a union if you know
	// how to use them.

	p = (char *) &ival;		// Make p point to i, without the type cast you
	// should get a warning.

	hashval = ival = 0;

	//I do not like this
	//maybe try something different as hash function
	for(i = 0; i < strlen(key); i += n)
	{
		strncpy(p, key + i, n);
		hashval += ival;
	}

	// divide by the number of elements and return our remainder
	return hashval % HASHELEMENTS;
}

//creates a new hash map structure
hash hm_create()
{
	hash ret = (hash)malloc(sizeof(hash_map));
	int i;
	ret->lists = (list*)malloc(HASHELEMENTS * sizeof(list));
	for(i = 0; i < HASHELEMENTS; i++)
		ret->lists[i] = NULL;

	return ret;
}

void hm_add(hash t, char* value)
{
	unsigned int hashval;
	hashval = hash_function(value);

	//printf("testes %d\n", hashval);

	if (t->lists[hashval] == NULL)
		t->lists[hashval] = ll_create();
	ll_add(t->lists[hashval], value);
}

//returns the linked list for a given key
int hm_find(hash t, char *key)
{
	unsigned int hashval;
	hashval = hash_function(key);

	if (t->lists[hashval] == NULL)
		return -1;

	if(ll_find(t->lists[hashval], key) >= 0)
		return hashval;

	return -1;
}

//removes the content of the table for a key
void hm_del(hash t, char *value)
{
	unsigned int hashval;

	hashval = hash_function(value);

	if (t->lists[hashval] == NULL)
		return;

	int pos = ll_find(t->lists[hashval], value);
	if(pos >= 0)
		ll_remove(t->lists[hashval], pos);
}

//provides statistics about table utilization
//might be useful for test different hash functions
void hm_profile(hash t)
{
	int max, total, i, nodes, used;
	double avg;
	list curhash;

	max = total = i = nodes = used = 0;
	avg = 0;

	if(t == NULL)
		return;

	for (i = 0; i < HASHELEMENTS; i++)
	{
		if (t->lists[i] != NULL)
		{
			used++;
			nodes = 0;
			curhash = t->lists[i]->next;

			while(curhash != NULL)
			{
				nodes++;
				curhash = curhash->next;
			}
			if (nodes != 0)
				total += nodes;
			if (nodes > max)
				max = nodes;

			fprintf(stderr, "hash[%d] contains %d elements\n", i, nodes);
		}
	}
	avg = (double)(total) / (double)(used);

	fprintf(stderr, "total: %d\n", total);
	fprintf(stderr, "max: %d\n", max);
	fprintf(stderr, "used: %lf\n", 100 * ((double)(used) / (double)(HASHELEMENTS)));
	fprintf(stderr, "average: %lf\n", avg);
}
